/*
 * Copyright 2008 M-Way Solutions
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.mwaysolutions.ocm.test;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.TreeMap;
import java.util.Vector;

import javax.jcr.NamespaceException;
import javax.jcr.NamespaceRegistry;
import javax.jcr.Node;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.SimpleCredentials;
import javax.jcr.Workspace;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.jackrabbit.core.TransientRepository;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

import com.mwaysolutions.ocm.IsSameItemEqualsWrapper;
import com.mwaysolutions.ocm.OcmProcessor;
import com.mwaysolutions.ocm.OcmReader;
import com.mwaysolutions.ocm.OcmRegister;
import com.mwaysolutions.ocm.OcmTypeRegister;
import com.mwaysolutions.ocm.OcmUtils;
import com.mwaysolutions.ocm.OcmWriter;

public class OcmTestCase {

	private static final Log log = LogFactory.getLog(OcmTestCase.class);
	private static Session session;
	private static OcmProcessor ocmProcessor;
	private static OcmTypeRegister ocmTypeRegister;
	private static Workspace workspace;
	
	
	@BeforeClass
	public static void init() throws IOException, RepositoryException {
		log.info("Initializing repository");
		
		final Repository repository = new TransientRepository();
		session = repository.login(new SimpleCredentials("Gofer", "".toCharArray()));
		workspace = session.getWorkspace();
        final String user = session.getUserID();
        final String name = repository.getDescriptor(Repository.REP_NAME_DESC);
        log.info("Logged in as " + user + " to a " + name + " repository.");
        ocmTypeRegister = new OcmTypeRegister();
		ocmProcessor = new OcmProcessor(ocmTypeRegister, new OcmRegister(ocmTypeRegister));
		
		
		log.info("Registering namespace");

		final NamespaceRegistry namespaceRegistry = workspace.getNamespaceRegistry();
		try {
			namespaceRegistry.getURI("gfr");
		} catch (final NamespaceException e) {
			log.debug("registering \"gfr\" namespace");
			namespaceRegistry.registerNamespace("gfr", "http://www.mwaysolutions.com/jcr/gfr");
		}

		
		log.info("Registering core types");
		
		ocmProcessor.registerType(workspace, Referenceable.class);

		ocmProcessor.registerType(workspace, HashMap.class, "gfr:HashMap");
		ocmProcessor.registerType(workspace, TreeMap.class, "gfr:TreeMap");
		ocmProcessor.registerType(workspace, ArrayList.class, "gfr:ArrayList");
		ocmProcessor.registerType(workspace, Vector.class, "gfr:Vector");
		ocmProcessor.registerType(workspace, HashSet.class, "gfr:HashSet");
		ocmProcessor.registerType(workspace, Stack.class, "gfr:Stack");

		
	}
	
	
	@AfterClass
	public static void destroy() {
		log.info("Logging out session");
        session.logout();
	}
		

	@Test
	public void testBasicBean() throws RepositoryException {
		final Node testNode = session.getRootNode().addNode("test");

		try {
			ocmProcessor.registerType(workspace, BeanWithPrimitive.class);

			final BeanWithPrimitive beanWithPrimitive = new BeanWithPrimitive();
			beanWithPrimitive.setString("hogofogo");
			beanWithPrimitive.setObjShort((short) 0);
			beanWithPrimitive.setPrimShort((short) 0);
			beanWithPrimitive.setObjInt(0);
			beanWithPrimitive.setPrimInt(0);
			beanWithPrimitive.setObjLong(0L);
			beanWithPrimitive.setPrimLong(0L);
			beanWithPrimitive.setObjFloat(0f);
			beanWithPrimitive.setPrimFloat(0f);
			beanWithPrimitive.setObjDouble(0d);
			beanWithPrimitive.setPrimDouble(0d);
			beanWithPrimitive.setObjByte((byte) 0);
			beanWithPrimitive.setPrimByte((byte) 0);
			beanWithPrimitive.setObjBoolean(false);
			beanWithPrimitive.setPrimBoolean(true);
			ocmProcessor.saveOrUpdate(testNode, "beanWithPrimitive", beanWithPrimitive);
			
			session.save();
			
			log.info("Reading beanWithPrimitive");
			OcmUtils.dump(ocmProcessor.get(testNode.getNode("beanWithPrimitive")), 2);
		} finally {
			testNode.remove();
			session.save();
		}
	}


	@Test
	public void testBasicArrayBean() throws RepositoryException {
		final Node testNode = session.getRootNode().addNode("test");

		try {
			ocmProcessor.registerType(workspace, BeanWithPrimitiveArray.class);

			final BeanWithPrimitiveArray beanWithPrimitiveArray = new BeanWithPrimitiveArray();
			beanWithPrimitiveArray.setStrings(new String[] {"aa", "bb", "cc"});
			beanWithPrimitiveArray.setObjShorts(new Short[] {(short) 1, (short) 2});
			beanWithPrimitiveArray.setPrimShorts(new short[] {(short) 3, (short) 4});
			beanWithPrimitiveArray.setObjInts(new Integer[] {11, 22});
			beanWithPrimitiveArray.setPrimInts(new int[] {33, 44});
			beanWithPrimitiveArray.setObjLongs(new Long[] {5L, 6L});
			beanWithPrimitiveArray.setPrimLongs(new long[] {7L, 8L});
			beanWithPrimitiveArray.setObjFloats(new Float[] {9.5f, 10.5f});
			beanWithPrimitiveArray.setPrimFloats(new float[] {11.5f, 12.5f});
			beanWithPrimitiveArray.setObjDoubles(new Double[] {609.5d, 610.5d});
			beanWithPrimitiveArray.setPrimDoubles(new double[] {611.5d, 612.5d});
			beanWithPrimitiveArray.setObjBytes(new Byte[] {21, 22});
			beanWithPrimitiveArray.setPrimBytes(new byte[] {23, 24});
			beanWithPrimitiveArray.setObjBooleans(new Boolean[] {true, false});
			beanWithPrimitiveArray.setPrimBooleans(new boolean[] {false, true});
			ocmProcessor.saveOrUpdate(testNode, "beanWithPrimitiveArray", beanWithPrimitiveArray);
			
			session.save();

			log.info("Reading beanWithPrimitiveArray");
			OcmUtils.dump(ocmProcessor.get(testNode.getNode("beanWithPrimitiveArray")), 2);
		} finally {
			testNode.remove();
			session.save();
		}
	}

	
	@Test
	public void testMix() throws RepositoryException {
		final Node testNode = session.getRootNode().addNode("test");

		try {
			log.info("Registering custom types");
			
			ocmProcessor.registerType(workspace, SimpleBean.class);
			ocmProcessor.registerType(workspace, SimpleBean[].class);
			ocmProcessor.registerType(workspace, BeanWithBeanArray.class);
			ocmProcessor.registerType(workspace, BeanWithBeanArray[].class);
			ocmProcessor.registerType(workspace, BeanWithListOfBeans.class);
			ocmProcessor.registerType(workspace, BeanWithListOfBeans[].class);
			
			log.info("Saving");

			final SimpleBean simpleBean = new SimpleBean();
			ocmProcessor.saveOrUpdate(testNode, "simpleBean", simpleBean);
			
			final SimpleBean[] simpleBeans = new SimpleBean[2];
			simpleBeans[0] = new SimpleBean();
			simpleBeans[1] = new SimpleBean();
			ocmProcessor.saveOrUpdate(testNode, "simpleBeans", simpleBeans);
			
	
			final BeanWithBeanArray beanWithBeanArray = new BeanWithBeanArray();
			beanWithBeanArray.setSimpleBeans(new SimpleBean[] { new SimpleBean(), new SimpleBean() });
			ocmProcessor.saveOrUpdate(testNode, "beanWithBeanArray", beanWithBeanArray);
	
			final List<SimpleBean> simpleBeanList = new ArrayList<SimpleBean>();
			simpleBeanList.add(new SimpleBean());
			simpleBeanList.add(new SimpleBean());
			ocmProcessor.saveOrUpdate(testNode, "simpleBeanList", simpleBeanList);
			
			final BeanWithListOfBeans beanWithListOfBeans = new BeanWithListOfBeans();
			beanWithListOfBeans.setSimpleBeanList(simpleBeanList);
			ocmProcessor.saveOrUpdate(testNode, "beanWithListOfBeans", beanWithListOfBeans);
			
			final Map<String, SimpleBean> simpleBeanMap = new HashMap<String, SimpleBean>();
			simpleBeanMap.put("key1", new SimpleBean());
			simpleBeanMap.put("key2", new SimpleBean());
			ocmProcessor.saveOrUpdate(testNode, "simpleBeanMap", simpleBeanMap);
			
			final Map<String, Integer> integerMap = new HashMap<String, Integer>();
			integerMap.put("key7", 7);
			integerMap.put("key8", 8);
			ocmProcessor.saveOrUpdate(testNode, "integerMap", integerMap);
	
			final Map<String, Object> mixMap = new HashMap<String, Object>();
			mixMap.put("key9", 7);
			mixMap.put("key10", new SimpleBean());
			mixMap.put("key11", new Float[] {1.2f, 1.5f});
			ocmProcessor.saveOrUpdate(testNode, "mixMap", mixMap);
	
			session.save();
	
			log.info("Dumping test node");
			OcmUtils.dump(session.getRootNode().getNode("test"), 2);
	
			log.info("Reading simpleBean");
			OcmUtils.dump(ocmProcessor.get(testNode.getNode("simpleBean")), 2);
		
			log.info("Reading mixMap");
			OcmUtils.dump(ocmProcessor.get(testNode.getNode("mixMap")), 2);
		} finally {
			testNode.remove();
			session.save();
		}
	}
	
	
	@Test
	public void testBeanWithReferenceArray() throws RepositoryException {
		final Node testNode = session.getRootNode().addNode("test");

		try {
			ocmProcessor.registerType(workspace, BeanWithReferenceArray.class);
			ocmProcessor.registerType(workspace, BeanWithReferenceArray[].class);
			
			final OcmWriter ocmWriter = new OcmWriter(ocmTypeRegister, session);
			
			final BeanWithReferenceArray bean1 = new BeanWithReferenceArray();
			final BeanWithReferenceArray bean2 = new BeanWithReferenceArray();
			final BeanWithReferenceArray bean3 = new BeanWithReferenceArray();

			bean1.setFollows(new BeanWithReferenceArray[] { bean2, bean3 });
			bean2.setFollows(new BeanWithReferenceArray[] { bean1 });
			
			ocmWriter.saveOrUpdate(testNode, "bean1", bean1, false, false);
			ocmWriter.saveOrUpdate(testNode, "bean2", bean2, false, false);
			ocmWriter.saveOrUpdate(testNode, "bean3", bean3, false, false);
			
			ocmWriter.saveDelayedReferences();
			
			OcmUtils.dump(testNode, 0);
			
			final OcmReader ocmReader = new OcmReader(ocmTypeRegister, new HashMap<IsSameItemEqualsWrapper, Object>());
			
			ocmReader.get(testNode.getNode("bean1"));
			
//			log.info("Dumping bean1");
//			OcmUtils.dump(obj, 0);

			
			session.save();
		} finally {
			testNode.remove();
			session.save();
		}
	}
	
	
	@Test
	public void testBeanWithReference() throws RepositoryException {
		final Node testNode = session.getRootNode().addNode("test");

		try {
			ocmProcessor.registerType(workspace, BeanWithReference.class);

			final BeanWithReference beanWithReference = new BeanWithReference();
			beanWithReference.setParent(beanWithReference);
			ocmProcessor.saveOrUpdate(testNode, "beanWithReference", beanWithReference);
			
			session.save();

			log.info("Reading beanWithReference");
			ocmProcessor.get(testNode.getNode("beanWithReference"));
		} finally {
			testNode.remove();
			session.save();
		}
	}

	
}
